package com.sss.myspringsecurity.config.securityconfig;

import com.sss.myspringsecurity.web.entity.SssMenu;
import com.sss.myspringsecurity.web.service.ISssMenuService;
import com.sss.myspringsecurity.config.securityconfig.dynaimcauthor.CustomizeAbstractSecurityInterceptor;
import com.sss.myspringsecurity.config.securityconfig.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

import java.util.List;

/**
 * springSecurity生效所需要的组件配置
 * @author sss
 * @date 2020-02-09 20:12
 **/
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ISssMenuService sssMenuService;
    /**
     * 用户验证处理器
    **/
    @Autowired
    private UserDetailsService userDetailService;

    /**
     * 用户退出处理器
    **/
    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

    /**
     *
     *用户登录失败处理器
    **/
    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
   private SessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    /**
     * Token验证处理器
    **/
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 权限异常处理器
     **/
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;


    /**
     * 访问决策管理器
     **/
    @Autowired
    private AccessDecisionManager accessDecisionManager;


    /**
     * 实现权限拦截
     **/
    @Autowired
   private FilterInvocationSecurityMetadataSource securityMetadataSource;

    /**
     * 实现权限拦截
     **/
    @Autowired
    private CustomizeAbstractSecurityInterceptor securityInterceptor;



    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        List<SssMenu> menuList = sssMenuService.list();
        http
                // CRSF禁用，因为不使用session
                .csrf().disable().exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()

                .sessionManagement()
                // 基于token，所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                o.setAccessDecisionManager(accessDecisionManager);//决策管理器
                o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
                return o;

            }
        })
                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers("/user/login", "/captchaImage","/websocket/**").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/static/css/*.css",
                        "/static/*.js",
                        "/templates/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/websocket/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                .antMatchers("/websocket/**").anonymous()

                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();


        http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class);
        // 添加JWT filter
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(userDetailService).passwordEncoder(bCryptPasswordEncoder());
    }
}
